package com.qipay.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.qipay.baas.model.*;
import com.qipay.cp.CpService;
import com.qipay.cpaccount.CpAccountService;
import com.qipay.mapper.CpAccountTradlogMapper;
import com.qipay.mapper.OrderMapper;
import com.qipay.notify.NotifyLogService;
import com.qipay.order.config.OrderSettleStateEnum;
import com.qipay.order.config.OrderStateEnum;
import com.qipay.order.service.OrderService;
import com.qipay.order.vo.*;
import com.qipay.qipay.QipayTradlogService;
import com.qipay.sp.SpChannelRuleService;
import com.qipay.sp.SpChannelService;
import com.qipay.sp.SpConfigService;
import com.qipay.sp.SpService;
import com.qipay.utils.DateUtils;
import com.qipay.utils.LocalCache;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Creator: Yao
 * Date:    2017/9/21
 * For:
 * Other:
 */
@Service
public class OrderServiceImpl implements OrderService {

    protected final Log logger = LogFactory.getLog(this.getClass());
    private static Integer count = 9999;
    @Autowired
    SpConfigService spConfigService;

    @Autowired
    OrderMapper orderMapper;

    @Autowired
    CpAccountService cpAccountService;

    @Autowired
    QipayTradlogService qipayTradlogService;

    @Autowired
    NotifyLogService notifyLogService;
    @Autowired
    CpAccountTradlogMapper cpAccountTradlogMapper;
    @Autowired
    SpChannelService spChannelService;

    @Autowired
    CpService cpService;

    @Autowired
    SpService spService;

    @Autowired
    SpChannelRuleService spChannelRuleService;

    @Override
    public Long sumOrderByCp(Long cpId) {
        return orderMapper.sumOrderByCp(cpId);
    }

    @Override
    public Long sumDisOrderByCp(Long cpId) {
        return orderMapper.sumDisOrderByCp(cpId);
    }

    @Override
    public Long sumOrderBySpUser(String sp_user_name, String form, String to) {
        return orderMapper.sumOrderBySpUser(sp_user_name, form, to);
    }

    @Override
    public String createOrder() {
        return UUID.randomUUID().toString().replace("-", "");
    }

    @Override
    public int randomDecrease(int max) {
        if (max <= 0) return 0;
        else
            return (int) (Math.random() * (max - 1)) + 1;
    }

    @Override
    public synchronized String createOrder(int length) {
        if (count >= 99999) count = 9999;
        count++;
        if (length > 17) {
            return DateFormatUtils.format(new Date(), "yyyyMMddHHmmss") + count.toString();
        } else {
            return DateFormatUtils.format(new Date(), "yyyyMMdd") + count.toString();
            //return  RandomStringUtils.randomAlphanumeric(length);
        }
    }

    @Override
    public OrderSumVo sumOrder(String querysql) {
        return orderMapper.sumOrder(querysql);
    }

    @Override
    public long countBy(String querysql) {
        return orderMapper.countBy(querysql);
    }

    @Override
    public long amountCount(String querysql) {
        return orderMapper.amountCount(querysql);
    }

    @Override
    public long feeCount(String querysql) {
        return orderMapper.feeCount(querysql);
    }

    @Override
    public List<OrderListItem> listBy(String querysql, Object offset, Object rowss) {
        return orderMapper.listBy(querysql, offset, rowss);
    }
    @Override
    public List<OrderListItem> listBy(String querysql, String orderBy) {
        return orderMapper.listBy0(querysql,orderBy);
    }

    @Override
    public List<CpTjVo> listCp0Tj(String from, String to, Long cpid) {
        return orderMapper.listCp0Tj(from, to, cpid);
    }

    @Override
    public List<SpTjVo> listSp0Tj(String from, String to, Long spid) {
        return orderMapper.listSp0Tj(from, to, spid);
    }

    @Override
    public List<ChannelTjVo> listChannel0Tj(String from, String to, int channelid) {
        return orderMapper.listChannel0Tj(from, to, channelid);
    }


    @Override
    public List<ChannelSpTjVo> listChannelSp0Tj(String from, String to, String spid, int channelid) {
        return orderMapper.listChannelSp0Tj(from, to, spid, channelid);
    }

    @Override
    public List<Cp_SpTjVo> listCp_Sp0Tj(String from, String to, String cpid, int channelid) {
        return orderMapper.listCp_Sp0Tj(from, to, cpid, channelid);
    }

    @Override
    public Order get(Long id) {
        return orderMapper.selectByPrimaryKey(id);
    }

    @Override
    @Transactional
    public Order update(Order order) {

        // check
        Assert.notNull(order, "订单信息为空");

        orderMapper.updateByPrimaryKey(order);
        return order;
    }

    @Override
    @Transactional
    public boolean modify(Order order) {

        // check
        Assert.notNull(order, "订单信息为空");

        return orderMapper.updateByPrimaryKey(order) > 0;
    }

    @Override
    public Order insert(Order order) {
        orderMapper.insert(order);
        order.getId();
        return order;
    }

    @Override
    public Order getByCpidAndOutTradeNo(Long cpid, String outTradeNo) {
        return orderMapper.getByCpidAndOutTradeNo(cpid, outTradeNo);
    }

    @Override
    public Order getByCpidAndTradeNo(Long cpid, String tradeNo) {
        OrderExample orderExample = new OrderExample();
        orderExample.setLimit(1);
        orderExample.createCriteria().andCpIdEqualTo(cpid).andTradeNoEqualTo(tradeNo);
        List<Order> orders = orderMapper.selectByExample(orderExample);
        if (orders == null || orders.size() == 0) {
            return null;
        }
        return orders.get(0);
    }

    @Override
    public Order getBySpMchIdAndSpTradeNo(String spMchid, String spTradeNo) {
        OrderExample orderExample = new OrderExample();
        orderExample.setLimit(1);
        orderExample.createCriteria().andSpMchIdEqualTo(spMchid).andSpTradeNoEqualTo(spTradeNo);
        List<Order> orders = orderMapper.selectByExample(orderExample);
        if (orders == null || orders.size() == 0) {
            return null;
        }
        return orders.get(0);
    }

    @Override
    public Order getByTradeNo(String tradeNo) {
        return orderMapper.selectByTradeNo(tradeNo);
//        OrderExample orderExample = new OrderExample();
//        orderExample.setLimit(1);
//        orderExample.createCriteria().andTradeNoEqualTo(tradeNo);
//        List<Order> orders = orderMapper.selectByExample(orderExample);
//        if (orders == null || orders.size() == 0) {
//            return null;
//        }
//        return orders.get(0);
    }

    @Override
    @Transactional
    public List<Order> getByRemark(String tradeNo) {
        List<Order> orders = orderMapper.getByRemark(tradeNo);
        if (orders == null || orders.size() == 0) {
            return null;
        }
        return orders;
    }

    @Override
    @Transactional
    public List<Order> getByBodyLike(String tradeNo) {
        List<Order> orders = orderMapper.getByBodyLike(tradeNo);
        if (orders == null || orders.size() == 0) {
            return null;
        }
        return orders;
    }

    @Override
    public Order getSureOrder(String tradeNo) {
        return orderMapper.getSureOrder(tradeNo);
    }

    @Override
    @Transactional
    public List<Order> getByAmount(String posId, String amount) {
        OrderExample orderExample = new OrderExample();
        List<Short> shorts = new ArrayList<>();
        shorts.add(OrderStateEnum.NOTPAY.getType());
        shorts.add(OrderStateEnum.WAITING.getType());
        orderExample.createCriteria().andStateIn(shorts).andCreateTimeBetween(DateUtils.sdf.format(new Date(new Date().getTime() - 15 * 60 * 1000)), DateUtils.sdf.format(new Date())).andBodyLike(posId + "：" + amount);
        List<Order> orders = orderMapper.selectByExample(orderExample);
        if (orders == null || orders.size() == 0) {
            return orders;
        }
        return orders;
    }

    @Override
    @Transactional
    public Order setOrderSuccess(String tradeNo) {
        Order order = getByTradeNo(tradeNo);
        Assert.notNull(order, "找不到订单信息");

        if (Objects.equals(order.getState(), OrderStateEnum.SUCCESS.getType())) {
            return order;
        }

        order.setState(OrderStateEnum.SUCCESS.getType());
        order.setFinishTime(new Date());
        update(order);

        return order;
    }

    @Transactional
    @Override
    public Order setOrderSuccess(Long orderId, String spTradeNo) throws IOException {
        Assert.notNull(orderId, "找不到订单信息");

        Order order = get(orderId);
        return setOrderSuccess(order, spTradeNo);
    }

    @Transactional
    @Override
    public Order setOrderSuccess(Order order, String spTradeNo) throws IOException {
        Assert.notNull(order, "找不到订单信息");
        if (Objects.equals(order.getState(), OrderStateEnum.SUCCESS.getType())) {
            return order;
        }

        Assert.isTrue(Objects.equals(order.getState(), OrderStateEnum.NOTPAY.getType()) || Objects.equals(order.getState(), OrderStateEnum.TOSUCCESS.getType())
                        || Objects.equals(order.getState(), OrderStateEnum.WAITING.getType()),
                "错误的订单状态，当前订单状态为" + OrderStateEnum.getByType(order.getState()));

        Cp cp = cpService.getOneById(order.getCpId());
        Assert.notNull(cp, "找不到cp信息");

        if (StringUtils.isNotBlank(spTradeNo)) {
            order.setSpTradeNo(spTradeNo);
        }

        order.setState(OrderStateEnum.SUCCESS.getType());
        order.setFinishTime(new Date());
        // 增加回调订单的临时记录缓存
        // 记录5分钟 5分钟后自动清除 避免内存占用过多
        if (LocalCache.contains(order.getTradeNo())) {
            logger.info("当前订单已回调，订单号：" + order.getTradeNo());
        } else {
            LocalCache.setCache(order.getTradeNo(), order, 10);
            //cpAccountService.setOrderSuccess(order);
//            qipayTradlogService.setOrderSuccess(order);
            notifyLogService.setOrderSuccess(order, cp);
            spChannelRuleService.setOrderSuccess(order);
            orderMapper.updateByPrimaryKey(order);

            //判断余额是否充足，不足，禁用供号商
            /*Integer chongzhiSum = cpAccountTradlogMapper.selectSumSpUserFeeChongzhi(Long.valueOf(order.getDescription()));
            Order orderSum = orderMapper.selectSumSpUserFee(order.getDescription());
            Integer accountMoney =chongzhiSum-(orderSum.getAmount()-orderSum.getAdvanceAmount());
            if(accountMoney<=0){
                spUserMapper.updateState((byte)0,Integer.valueOf(order.getDescription()));
            }*/

        }
        return order;
    }


    @Override
    @Transactional
    public Order setOrderFail(String tradeNo) {
        Order order = getByTradeNo(tradeNo);
        Assert.notNull(order, "找不到订单信息");

        if (Objects.equals(order.getState(), OrderStateEnum.PAYERROR.getType())) {
            return order;
        }

        order.setState(OrderStateEnum.PAYERROR.getType());
        order.setFinishTime(new Date());
        update(order);

        return order;
    }

    @Override
    @Transactional
    public List<Order> getOrderFinishTimeLargeThenOneDay(int count, int page) {
        Date today = new Date();
        Date endDate = new Date(today.getYear(), today.getMonth(), today.getDate());

        int start = (page - 1) * count;

        OrderExample orderExample = new OrderExample();
        orderExample.setLimit(count);
        orderExample.setOffset(start);
        orderExample.createCriteria().andFinishTimeLessThan(endDate)
                .andStateEqualTo(OrderStateEnum.SUCCESS.getType())
                .andSettleStatusEqualTo(OrderSettleStateEnum.NOTSETTLE.getType());
        List<Order> orders = orderMapper.selectByExample(orderExample);
        return orders;
    }

    @Override
    @Transactional
    public Integer getOrderFinishTimeLargeThenOneDay() {
        Date today = new Date();
        Date endDate = new Date(today.getYear(), today.getMonth(), today.getDate());

        OrderExample orderExample = new OrderExample();
        orderExample.createCriteria().andFinishTimeLessThan(endDate)
                .andStateEqualTo(OrderStateEnum.SUCCESS.getType())
                .andSettleStatusEqualTo(OrderSettleStateEnum.NOTSETTLE.getType());
        return orderMapper.countByExample(orderExample);
    }

    @Override
    @Transactional
    public Order setOrderClose(Order order) {
        Assert.notNull(order, "找不到订单信息");
        if (Objects.equals(order.getState(), OrderStateEnum.SUCCESS.getType())) {
            return order;
        }

        Assert.isTrue(Objects.equals(order.getState(), OrderStateEnum.NOTPAY.getType()),
                "错误的订单状态，当前订单状态为" + OrderStateEnum.getByType(order.getState()));

        order.setState(OrderStateEnum.CLOSE.getType());
        order.setFinishTime(new Date());
        update(order);
        return order;
    }

//    @Override
//    @Transactional
//    public Order selectASp(Order order, ChannelTypeEnum channelTypeEnum) {
//        SpChannel spChannel = spChannelService.next(channelTypeEnum, order.getAmount());
//
//        order.setSpId(spChannel.getSpId());
//        order.setSpFeeRecharge(spChannel.getFeeRecharge());
//        order.setSpFee(spChannel.getFeeRecharge().multiply(
//                new BigDecimal(order.getAmount().toString())
//        ).intValue());
//        return order;
//    }

    /**
     * 记录spChanel的下单date，用于轮询，按顺序轮询
     */
    private final static Map<Integer,Date> spChannelTimes = new HashMap<>();
    /**
     * 记录spChanel的下单金额，用于金额均衡轮询
     * 说明：Byte--channelType，Map<Integer,List<SpChannel>>--金额，支持此金额的SpChannel集合
     * 下一单，移除一次SpChannel，直到全空，重新添加所有支持此金额的SpChannel集合
     */
    private final static Map<Byte,Map<Integer,List<SpChannel>>> spChannelAmount = new HashMap<>();
    @Override
    public Order selectASp(Order order, Byte channelType, List<Long> spIds) {
        List<SpChannel> spChannels = spChannelService.next_new(channelType,order.getAmount(), spIds);
        Assert.notEmpty(spChannels, "找不到可用的支付通道");
//        spChannel.setUpdateDate(new Date());
//        spChannelService.update(spChannel);
        int amount = new BigDecimal(order.getAmount()).divide(new BigDecimal("100"), 2, BigDecimal.ROUND_DOWN).intValue();
        List<SpChannel> resultSpList = spChannels.parallelStream().filter(spChannel -> {
            Date spChanelDate = spChannelTimes.get(spChannel.getId());
            spChannel.setUpdateDate(spChanelDate==null?new Date(0):spChanelDate);

            String quota = spChannel.getQuota();
            if (StringUtils.isNotBlank(quota)) {
                if (quota.contains("-")) {
                    String[] quotas = quota.split("，");
                    String[] amounts = quotas[0].split("-");
                    if (quotas.length>1&&quotas[1].equals("整百")) {
                        return amount >= Integer.parseInt(amounts[0]) && amount <= Integer.parseInt(amounts[1]) && amount % 100 == 0;
                    } else {
                        return amount >= Integer.parseInt(amounts[0]) && amount <= Integer.parseInt(amounts[1]);
                    }
                } else {
                    quota = "," + quota.replace("，", ",") + ",";
                    return quota.contains("," + amount + ",");
                }
            } else {
                return true;
            }
        }).collect(Collectors.toList());
        Assert.notEmpty(resultSpList, "找不到对应金额的支付通道");

        //*****************金额均衡机制（个码调单辅助机制）+通道按顺序轮询机制******************
       /* Map<Integer,List<SpChannel>> amount_spChannels = spChannelAmount.get(channelType);
        if(amount_spChannels==null)amount_spChannels = new HashMap<>();

        List<SpChannel> spChannels1 = amount_spChannels.get(order.getAmount());
        if(spChannels1==null)spChannels1 = resultSpList;

        //按date进行排序
        List<SpChannel> newSpList = spChannels1.stream().sorted(Comparator.comparing(SpChannel::getUpdateDate))
                .collect(Collectors.toList());
        SpChannel spChannelLast = null;
        //校验sp是否被禁用
        for(Iterator<SpChannel>iterator = newSpList.iterator();iterator.hasNext();){
            SpChannel sc = iterator.next();
            if(resultSpList.contains(sc)){
                spChannelLast = sc;
                break;
            }else {
                iterator.remove();
            }
        }
        if(spChannelLast==null){
            spChannels1 = resultSpList;
            newSpList = spChannels1.stream().sorted(Comparator.comparing(SpChannel::getUpdateDate))
                    .collect(Collectors.toList());
            spChannelLast = newSpList.get(0);
        }

        //录入最近一次使用的spChanel的索引和date
        spChannelTimes.put(spChannelLast.getId(),new Date());
        //去掉已经轮询过的金额spChanel，更新缓存
        spChannels1.remove(spChannelLast);
        amount_spChannels.put(order.getAmount(),spChannels1);
        spChannelAmount.put(channelType,amount_spChannels);*/
        //*****************金额均衡机制+通道按顺序轮询机制************结束**结束**结束**

        //*****************单独通道按顺序轮询机制******************
        //按date进行排序
        List<SpChannel> newSpList = resultSpList.stream().sorted(Comparator.comparing(SpChannel::getUpdateDate))
                .collect(Collectors.toList());
        SpChannel spChannelLast = newSpList.get(0);
        //录入最近一次使用的spChanel的索引和date
        spChannelTimes.put(spChannelLast.getId(),new Date());

        order.setSpId(spChannelLast.getSpId());
        order.setSpMchId(spChannelLast.getSpCallbackIp());
        order.setSpFeeRecharge(spChannelLast.getFeeRecharge());
        order.setSpFee(spChannelLast.getFeeRecharge().multiply(new BigDecimal(order.getAmount().toString())).intValue());
        return order;
    }

    @Override
    public List<Order> statementBy(Long cpId, String from, String to, Integer channel) {

        return orderMapper.statementBy(cpId, from, to, channel);
    }

    @Override
    public List<OrderStatementVo> statementAllBy(String from, String to, Long cpId, String cpName, Long agentId, String contract, Integer channel) {

        return orderMapper.statementAllBy(from, to, cpId, cpName, agentId, contract, channel);
    }

    @Override
    public List<OrderSpStatementVo> statementSpBy(String from, String to, Long spId, String spName, String service, Integer channel) {
        return orderMapper.statementSpBy(from, to, spId, spName, service, channel);
    }

    @Override
    public Order byState(Byte state) {
        return orderMapper.byState(state);
    }

    @Override
    public int insertTbOrder(TbVO tbVO) {
        return orderMapper.insertTbOrder(tbVO);
    }

    @Override
    public void updateTbOrder(String id, String tbId, String qrCode) {
        orderMapper.updateTbOrder(id, tbId, qrCode);
    }

    @Override
    public Map<String, String> selectTbQrCode(String amount) {
        return orderMapper.selectTbQrCode(amount);
    }

    @Override
    public Map<String, Object> queryOrder(String orderNo,Long spId) {
        Map<String, Object> spConfigs = spConfigService.getKeyValues(spId);
        String pid = spConfigs.get("pid").toString();
        String key = spConfigs.get("key").toString();
        String query_url = spConfigs.get("query_url").toString()+"?act=order&pid="+pid+"&key="+key+"&out_trade_no="+orderNo;
        String result = null;
        try {
            result = sendGetMethod(query_url,"UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        JSONObject jsonObject = JSONObject.parseObject(result);
        return jsonObject;
    }

    @Override
    public void updateTbType(String id) {
        orderMapper.updateTbType(id);
    }

    @Override
    public void updateTbTypeByTbId(String tbId) {
        orderMapper.updateTbTypeByTbId(tbId);
    }

    @Override
    public List<Order> searchZzOrder() {
        return orderMapper.searchZzOrder();
    }

    @Override
    public List<Order> searchPddSureOrder() {
        return orderMapper.searchPddSureOrder();
    }

    @Override
    public List<Order> selectBySpidTime(Long spid, String startTime) {
        return orderMapper.selectBySpidTime(spid,startTime);
    }

    @Override
    public List<Order> selectBySpidAmountTime(Long spid,Integer amount) {
        OrderExample orderExample = new OrderExample();
        List<Short> shorts = new ArrayList<>();
        shorts.add(OrderStateEnum.NOTPAY.getType());
        shorts.add(OrderStateEnum.WAITING.getType());
        orderExample.createCriteria()
                .andStateIn(shorts).andCreateTimeBetween(DateUtils.sdf.format(new Date(new Date().getTime() - 5 * 60 * 1000)),
                DateUtils.sdf.format(new Date()))
                .andSpIdEqualTo(spid).andAmountEqualTo(amount);
        List<Order> orders = orderMapper.selectByExample(orderExample);
        if (orders == null || orders.size() == 0) {
            return orders;
        }
        return orders;
    }

    public String sendGetMethod(String url,String encoding) throws Exception {
        String result = "";

        HttpClient client = new DefaultHttpClient();
        HttpGet get = new HttpGet(url);
        get.setHeader("charset", encoding);

        try {
            HttpResponse response = client.execute(get);
            if (200 == response.getStatusLine().getStatusCode()) {
                result = EntityUtils.toString(response.getEntity(), encoding);
            } else {
                throw new Exception("Invalide response from Api!"
                        + response.toString());
            }
        } catch (ClientProtocolException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return result;
    }
}
